#ifndef	__objects_h__
#define	__objects_h__

#include "math/MathLib.h"

struct RayHitInfo
{
	float			distToHit;
	rtMath::vec3	hitPos;
	rtMath::vec3	hitNormal;
	rtMath::vec2	hitTexcoord;
};

class SceneObject
{
public:
	enum
	{
		ObjectTypeDummy	=	0,
		ObjectTypeShape,
		ObjectTypeLight,
		ObjectTypeCamera
	};

public:
	SceneObject(int type = ObjectTypeDummy) : m_ObjectType(type) {}
	virtual ~SceneObject() {}

	int							GetObjectType(void) const { return m_ObjectType; }
	void						SetName(const rtString& name) { m_ObjectName = name; }
	const rtString&				GetName(void) const { return m_ObjectName; }

	virtual void				SetPosition(const rtMath::vec3& pos) { m_Position = pos; }
	virtual const rtMath::vec3&	GetPosition(void) const { return m_Position; }

protected:
	int				m_ObjectType;
	rtString		m_ObjectName;
	rtMath::vec3	m_Position;
};

class Material
{
public:
	Material();
	~Material();

	void					SetName(const rtString& name);
	const rtString&			GetName(void) const;
	void					SetDiffuse(const rtMath::vec3& diffuse);
	const rtMath::vec3&		GetDiffuse(void) const;
	void					SetSpecular(const rtMath::vec3& specular);
	const rtMath::vec3&		GetSpecular(void) const;
	void					SetSpecularLevel(float specLevel);
	float					GetSpecularLevel(void) const;
	void					SetGlossiness(float glossiness);
	float					GetGlossiness(void) const;
	void					SetTransparency(float transparency);
	float					GetTransparency(void) const;
	void					SetReflectivity(float reflectivity);
	float					GetReflectivity(void) const;
	void					SetRefractionIndex(float reflraction);
	float					GetRefractionIndex(void) const;
	void					SetAlbedoTexture(int albedoTex);
	int						GetAlbedoTexture(void) const;
	void					SetBumpTexture(int bumpTex);
	int						GetBumpTexture(void) const;

private:
	rtString			m_Name;
	rtMath::vec3		m_Diffuse;
	rtMath::vec3		m_Specular;
	float				m_SpecularLevel;
	float				m_Glossiness;
	float				m_Transparency;
	float				m_Reflectivity;
	float				m_RefractionIndex;
	int					m_AlbedoTexture;
	int					m_BumpTexture;
};


class SceneShape : public SceneObject
{
public:
	enum
	{
		ShapeEmpty = 0,
		ShapeSphere,
		ShapeBox,
		ShapePlane
	};

public:
	SceneShape(int type = ShapeEmpty)
		: SceneObject(SceneObject::ObjectTypeShape)
		, m_ShapeType(type)
		, m_MaterialID(-1)
	{}
	virtual ~SceneShape() {}

	int				GetShapeType(void) const { return m_ShapeType; }
	void			SetMaterialID(const int materialID) { m_MaterialID = materialID; }
	int				GetMaterialID(void) const { return m_MaterialID; }

	virtual bool	RayCollisionCheck(const rtMath::Ray& ray, RayHitInfo* hitInfo = NULL, float t = rtMath::MAX_FLOAT) const { return false; }

protected:
	int			m_ShapeType;
	int			m_MaterialID;
};


class ShapeSphere : public SceneShape
{
public:
	ShapeSphere();
	~ShapeSphere();

	bool			RayCollisionCheck(const rtMath::Ray& ray, RayHitInfo* hitInfo = NULL, float t = rtMath::MAX_FLOAT) const;

	void			SetRadius(float radius);
	float			GetRadius(void) const;

private:
	rtMath::vec2	CalculateTexcoords(const rtMath::vec3& pos) const;

private:
	float		m_Radius;
};

class ShapeBox : public SceneShape
{
public:
	ShapeBox();
	~ShapeBox();

	bool				RayCollisionCheck(const rtMath::Ray& ray, RayHitInfo* hitInfo = NULL, float t = rtMath::MAX_FLOAT) const;

	void				SetMinimum(const rtMath::vec3& minimum);
	const rtMath::vec3&	GetMinimum(void) const;
	void				SetMaximum(const rtMath::vec3& maximum);
	const rtMath::vec3&	GetMaximum(void) const;

private:
	rtMath::vec2		CalculateTexcoords(const rtMath::vec3& pos, int normAxis) const;

private:
	rtMath::vec3		m_MinMax[2];
};

class ShapePlane : public SceneShape
{
public:
	ShapePlane();
	~ShapePlane();

	bool	RayCollisionCheck(const rtMath::Ray& ray, RayHitInfo* hitInfo = NULL, float t = rtMath::MAX_FLOAT) const;

	void					SetNormal(const rtMath::vec3& normal);
	const rtMath::vec3&		GetNormal(void) const;
	void					SetDistance(float distance);
	float					GetDistance(void) const;
	void					Setup(void);
	const rtMath::vec3&		GetUAxis(void) const;
	const rtMath::vec3&		GetVAxis(void) const;

private:
	rtMath::vec3		m_Normal;
	float				m_Distance;
	rtMath::vec3		m_UAxis;
	rtMath::vec3		m_VAxis;
};


class SceneLight : public SceneObject
{
public:
	SceneLight();
	~SceneLight();

	void				SetIntensity(const rtMath::vec3& intensity);
	const rtMath::vec3&	GetIntensity(void) const;

private:
	rtMath::vec3	m_Intensity;
};


class SceneCamera : public SceneObject
{
public:
	SceneCamera();
	~SceneCamera();

	void					SetDirection(const rtMath::vec3& dir);
	const rtMath::vec3&		GetDirection(void) const;
	void					SetUpVector(const rtMath::vec3& dir);
	const rtMath::vec3&		GetUpVector(void) const;
	void					SetFOV(float fov);
	float					GetFOV(void) const;
	void					SetNearClip(float nearClip);
	float					GetNearClip(void) const;
	void					SetFarClip(float farClip);
	float					GetFarClip(void) const;

	const rtMath::vec3&		GetU(void) const	{ return m_U; }
	const rtMath::vec3&		GetV(void) const	{ return m_V; }

	void					Setup(float width, float height);
	rtMath::Ray				ComputeRay(float x, float y, float width, float height) const;

private:
	rtMath::vec3	m_Direction;
	rtMath::vec3	m_UpVector;
	float			m_FOV;
	float			m_NearClip;
	float			m_FarClip;

	// iOrange - 
	rtMath::vec3	m_U;
	rtMath::vec3	m_V;
};


#endif	// __objects_h__
